from threading import Thread
from select import select
from pickle import dumps
import struct

class BBWorker(Thread):
    """
    This worker thread is spawned every time a new connection to the tracker is made.
    There are only a handful of commands:
       PEER REQUEST <bid>: A request for some peers sharing the given bid.
       REGISTER <bid>: A registration that the connecting peer is sharing the bid.
       LEAVE <bid>: Information that the peer is leaving, i.e., is no longer actively
                    sharing that bid. 
       UPDATE <bid>: An update notifying the tracker that the issuing peer is still 
                     actively sharing the bid. This must be issued periodically by 
                     the client to stay in the trackers tables.
    
    The format of command packets is such that the first 16 bytes are reserved for a 
    command string, and the next 40 bytes are reserved for the id.
    """
    
    TIMEOUT = 5.0

    def __init__(self, conn, addr, tracker):
        super(BBWorker, self).__init__()
        self._conn = conn
        self._addr = addr
        self._tracker = tracker

    def do_request(self, bid, port):
        print 'REQUEST'
        try:
            self._tracker.update_brook_activity(bid)
            peers = self._tracker.request_peers(bid, (self._addr[0], port))
            self._conn.send(dumps(peers, -1))
        except Exception, e:
            self.nack(e.args[0])
    
    def do_register(self, bid, port):
        print 'REGISTER'
        try:
            self._tracker.register_brook(bid, (self._addr[0], port))
            self.ack()
        except Exception, e:
            self.nack(e.args[0])

    def do_leave(self, bid, port):
        print 'LEAVE'
        try:
            self._tracker.update_brook_activity(bid)
            self._tracker.remove_peer(bid, (self._addr[0], port))
            self.ack()
        except Exception, e:
            self.nack(e.args[0])

    def do_update(self, bid, port):
        print 'UPDATE'
        try:
            self._tracker.update_brook_activity(bid)
            self._tracker.update_peer_activity(bid, (self._addr[0], port))
            self.ack()
        except Exception, e:
            self.nack(e.args[0])

    def nack(self, reason = ""):
        self._conn.send("NACK %s"%reason)

    def ack(self):
        self._conn.send("ACK")
        
    def run(self):
        shutdown = False
        while True:
            # Wait for a couple of seconds for a command to arrive.
            (rd, _, _) = select([self._conn], [], [], BBWorker.TIMEOUT)
            if rd != []:
                # A command has arrived - parse it.
                data = self._conn.recv(1024)
                
                # Check whether the connection has been closed.
                if data == "":
                    break

                # Unpack the command and bid.
                try:
                    cmd, bid, port = struct.unpack("!16s40sH", data)
                except:
                    # Error unpacking command - wrong format. Send an error message back.
                    self.nack("Invalid command format.")
                    continue

                # Split out depending on the command received.
                cmd = cmd.strip("\0").upper()
                if cmd == "PEER REQUEST":
                    self.do_request(bid, port)
                elif cmd == "REGISTER":
                    self.do_register(bid, port)
                elif cmd == "LEAVE":
                    self.do_leave(bid, port)
                elif cmd == "UPDATE":
                    self.do_update(bid, port)
                else:
                    # Unknown command.
                    self.nack("Unknown command %s"%cmd)
                    continue
            
            else:
                # Timeout - we only allow one of those before closing the connection.
                if shutdown:
                    break
                else:
                    shutdown = True

        # Close the connection.
        self._conn.close()

                
                    
